Tegra210: support for cluster idle from the CPU
authorVarun Wadekar <[email protected]>
Wed, 14 Feb 2018 04:31:12 +0000 (20:31 -0800)
committerVarun Wadekar <[email protected]>
Thu, 31 Jan 2019 16:48:09 +0000 (08:48 -0800)
This patch adds support to enter/exit to/from cluster idle power
state on Tegra210 platforms that do not load BPMP firmware.

The CPU initates the cluster idle sequence on the last standing
CPU, by following these steps:

Entry
-----
* stop other CPUs from waking up
* program the PWM pinmux to tristate for OVR PMIC
* program the flow controller to enter CC6 state
* skip L1 $ flush during cluster power down, as L2 $ is inclusive
  of L1 $ on Cortex-A57 CPUs

Exit
----
* program the PWM pinmux to un-tristate for OVR PMIC
* allow other CPUs to wake up

This patch also makes sure that cluster idle state entry is not
enabled until CL-DVFS is ready.

Change-Id: I54cf31bf72b4a09d9bf9d2baaed6ee5a963c7808
Signed-off-by: Varun Wadekar <[email protected]>
plat/nvidia/tegra/include/t210/tegra_def.h
plat/nvidia/tegra/soc/t210/plat_psci_handlers.c
plat/nvidia/tegra/soc/t210/platform_t210.mk

index 02a49b8f585a857b9f62134af93ee0410ba36a82..7eb8a878df9f09eeffcebace61c944ecb849715f 100644 (file)
  ******************************************************************************/
 #define TEGRA_MISC_BASE                        U(0x70000000)
 #define  HARDWARE_REVISION_OFFSET      U(0x804)
+#define  PINMUX_AUX_DVFS_PWM           U(0x3184)
+#define  PINMUX_PWM_TRISTATE           (U(1) << 4)
 
 /*******************************************************************************
  * Tegra UART controller base addresses
 #define MC_SMMU_PPCS_ASID_0            0x270U
 #define  PPCS_SMMU_ENABLE              (0x1U << 31)
 
+/*******************************************************************************
+ * Tegra CLDVFS constants
+ ******************************************************************************/
+#define TEGRA_CL_DVFS_BASE             U(0x70110000)
+#define DVFS_DFLL_CTRL                 U(0x00)
+#define  ENABLE_OPEN_LOOP              U(1)
+#define  ENABLE_CLOSED_LOOP            U(2)
+#define DVFS_DFLL_OUTPUT_CFG           U(0x20)
+#define  DFLL_OUTPUT_CFG_I2C_EN_BIT    (U(1) << 30)
+#define  DFLL_OUTPUT_CFG_CLK_EN_BIT    (U(1) << 6)
+
 /*******************************************************************************
  * Tegra SE constants
  ******************************************************************************/
index f52d975d833534c8d12f7ee41acd7a0f4fd214a6..0aa36b4ef8fba5ffa461ddca65085aa3b9640754 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -35,6 +35,7 @@
 #define SCLK_BURST_POLICY_DEFAULT      0x10000000
 
 static int cpu_powergate_mask[PLATFORM_MAX_CPUS_PER_CLUSTER];
+static bool tegra_bpmp_available = true;
 
 int32_t tegra_soc_validate_power_state(unsigned int power_state,
                                        psci_power_state_t *req_state)
@@ -53,11 +54,12 @@ int32_t tegra_soc_validate_power_state(unsigned int power_state,
 
        case PSTATE_ID_CLUSTER_IDLE:
        case PSTATE_ID_CLUSTER_POWERDN:
+
                /*
-                * Cluster powerdown/idle request only for afflvl 1
+                * Cluster idle request for afflvl 0
                 */
-               req_state->pwr_domain_state[MPIDR_AFFLVL1] = state_id;
                req_state->pwr_domain_state[MPIDR_AFFLVL0] = PSTATE_ID_CORE_POWERDN;
+               req_state->pwr_domain_state[MPIDR_AFFLVL1] = state_id;
 
                break;
 
@@ -83,7 +85,7 @@ int32_t tegra_soc_validate_power_state(unsigned int power_state,
 
 /*******************************************************************************
  * Platform handler to calculate the proper target power level at the
- * specified affinity level
+ * specified affinity level.
  ******************************************************************************/
 plat_local_state_t tegra_soc_get_target_pwr_state(unsigned int lvl,
                                             const plat_local_state_t *states,
@@ -92,7 +94,7 @@ plat_local_state_t tegra_soc_get_target_pwr_state(unsigned int lvl,
        plat_local_state_t target = PSCI_LOCAL_STATE_RUN;
        int cpu = plat_my_core_pos();
        int core_pos = read_mpidr() & MPIDR_CPU_MASK;
-       uint32_t bpmp_reply, data[3];
+       uint32_t bpmp_reply, data[3], val;
        int ret;
 
        /* get the power state at this level */
@@ -109,9 +111,40 @@ plat_local_state_t tegra_soc_get_target_pwr_state(unsigned int lvl,
 
                        /* Cluster idle not allowed */
                        target = PSCI_LOCAL_STATE_RUN;
+
+                       /*******************************************
+                        * BPMP is not present, so handle CC6 entry
+                        * from the CPU
+                        ******************************************/
+
+                       /* check if cluster idle state has been enabled */
+                       val = mmio_read_32(TEGRA_CL_DVFS_BASE + DVFS_DFLL_CTRL);
+                       if (val == ENABLE_CLOSED_LOOP) {
+                               /*
+                                * flag to indicate that BPMP firmware is not
+                                * available and the CPU has to handle entry/exit
+                                * for all power states
+                                */
+                               tegra_bpmp_available = false;
+
+                               /*
+                                * Acquire the cluster idle lock to stop
+                                * other CPUs from powering up.
+                                */
+                               tegra_fc_ccplex_pgexit_lock();
+
+                               /* Cluster idle only from the last standing CPU */
+                               if (tegra_pmc_is_last_on_cpu() && tegra_fc_is_ccx_allowed()) {
+                                       /* Cluster idle allowed */
+                                       target = PSTATE_ID_CLUSTER_IDLE;
+                               } else {
+                                       /* release cluster idle lock */
+                                       tegra_fc_ccplex_pgexit_unlock();
+                               }
+                       }
                } else {
 
-                       /* Cluster idle */
+                       /* Cluster power-down */
                        data[0] = (uint32_t)cpu;
                        data[1] = TEGRA_PM_CC6;
                        data[2] = TEGRA_PM_SC1;
@@ -120,10 +153,10 @@ plat_local_state_t tegra_soc_get_target_pwr_state(unsigned int lvl,
                                        (void *)&bpmp_reply,
                                        (int)sizeof(bpmp_reply));
 
-                       /* check if cluster idle entry is allowed */
+                       /* check if cluster power down is allowed */
                        if ((ret != 0L) || (bpmp_reply != BPMP_CCx_ALLOWED)) {
 
-                               /* Cluster idle not allowed */
+                               /* Cluster power down not allowed */
                                target = PSCI_LOCAL_STATE_RUN;
                        }
                }
@@ -176,7 +209,9 @@ int tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state)
        unsigned int stateid_afflvl2 = pwr_domain_state[MPIDR_AFFLVL2];
        unsigned int stateid_afflvl1 = pwr_domain_state[MPIDR_AFFLVL1];
        unsigned int stateid_afflvl0 = pwr_domain_state[MPIDR_AFFLVL0];
+       uint32_t cfg;
        int ret = PSCI_E_SUCCESS;
+       uint32_t val;
 
        if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) {
 
@@ -197,6 +232,17 @@ int tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state)
 
                assert(stateid_afflvl0 == PSTATE_ID_CORE_POWERDN);
 
+               if (!tegra_bpmp_available) {
+
+                       /* PWM tristate */
+                       cfg = mmio_read_32(TEGRA_CL_DVFS_BASE + DVFS_DFLL_OUTPUT_CFG);
+                       if (cfg & DFLL_OUTPUT_CFG_CLK_EN_BIT) {
+                               val = mmio_read_32(TEGRA_MISC_BASE + PINMUX_AUX_DVFS_PWM);
+                               val |= PINMUX_PWM_TRISTATE;
+                               mmio_write_32(TEGRA_MISC_BASE + PINMUX_AUX_DVFS_PWM, val);
+                       }
+               }
+
                /* Prepare for cluster idle */
                tegra_fc_cluster_idle(mpidr);
 
@@ -245,6 +291,7 @@ int tegra_soc_pwr_domain_power_down_wfi(const psci_power_state_t *target_state)
 int tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state)
 {
        const plat_params_from_bl2_t *plat_params = bl31_get_plat_params();
+       uint32_t cfg;
        uint32_t val;
 
        /* platform parameter passed by the previous bootloader */
@@ -286,7 +333,29 @@ int tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state)
                 * Restore Boot and Power Management Processor (BPMP) reset
                 * address and reset it.
                 */
-               tegra_fc_reset_bpmp();
+               if (tegra_bpmp_available)
+                       tegra_fc_reset_bpmp();
+       }
+
+       /*
+        * Check if we are exiting cluster idle state
+        */
+       if (target_state->pwr_domain_state[MPIDR_AFFLVL1] ==
+                       PSTATE_ID_CLUSTER_IDLE) {
+
+               if (!tegra_bpmp_available) {
+
+                       /* PWM un-tristate */
+                       cfg = mmio_read_32(TEGRA_CL_DVFS_BASE + DVFS_DFLL_OUTPUT_CFG);
+                       if (cfg & DFLL_OUTPUT_CFG_CLK_EN_BIT) {
+                               val = mmio_read_32(TEGRA_MISC_BASE + PINMUX_AUX_DVFS_PWM);
+                               val &= ~PINMUX_PWM_TRISTATE;
+                               mmio_write_32(TEGRA_MISC_BASE + PINMUX_AUX_DVFS_PWM, val);
+                       }
+
+                       /* release cluster idle lock */
+                       tegra_fc_ccplex_pgexit_unlock();
+               }
        }
 
        /*
index 59077eb81cd93d9617dcc367bffe13eb293d3cd1..723534a1d78fc26f95315a8b937f9d219337f5ba 100644 (file)
@@ -51,3 +51,6 @@ A53_DISABLE_NON_TEMPORAL_HINT :=      1
 ERRATA_A53_826319              :=      1
 ERRATA_A53_836870              :=      1
 ERRATA_A53_855873              :=      1
+
+# Skip L1 $ flush when powering down Cortex-A57 CPUs
+SKIP_A57_L1_FLUSH_PWR_DWN      :=      1